#include <iostream>
#include <fstream>
#include <string>
#include <queue>
#include <semaphore.h>
#include <thread>
#include <mutex>
#include <unistd.h>

using namespace std;

string currentTime()
{
    time_t timep;
    struct tm *p;
    time(&timep);          // 获取从1970至今过了多少秒，存入time_t类型的timep
    p = localtime(&timep); // 用localtime将秒数转化为struct tm结构体
    string str = "";
    str += to_string(1900 + p->tm_year) + "-" + to_string(1 + p->tm_mon) + "-" + to_string(p->tm_mday) + "-" + to_string(p->tm_hour) + "-" + to_string(p->tm_min) + "-" + to_string(p->tm_sec);
    return str;
}

enum class log_level
{
    debug,
    info,
    warn,
    error
};

enum class log_target
{
    terminal,
    file,
    all
};

struct logInfo
{
    log_level ll;
    string msg;
};


class log
{
private:
    void insert(string msg,log_level l1);
    void write(string msg, log_level l1);
    log(log_target l1 = log_target::all, log_level l2 = log_level::error, string p = "");

public:
    static log *getInstance(log_target l1 = log_target::all, log_level l2 = log_level::error, string p = "");
    static void deleteInstance();

public:
    void debug(string msg) { insert(msg,log_level::debug); }
    void info(string msg) { insert(msg,log_level::info); }
    void warn(string msg) { insert(msg,log_level::warn); }
    void error(string msg) { insert(msg,log_level::error); }
    void run();
    void stop();
    void start();
    ~log() = delete;

public:
    
    ofstream outfile;
    log_level ll;
    log_target lt;
    string path;
    bool running;
    //bool ending;
private: 
    queue<logInfo> qu;
    static log *log_ptr;
    static mutex mtx;
};

log::log(log_target l1, log_level l2, string p)
{
    running = false;
    //ending = false;
    lt = l1;
    ll = l2;
    path = p;
    string m = "[Welcome]     " + currentTime() + " : " + "=== Start logging ===\n";
    if (lt != log_target::terminal)
    {
        if (p.empty())
            path = currentTime() + ".log";
        outfile.open(path, ios::app);
        if (outfile.is_open())
            outfile << m;
    }
    if (lt != log_target::file)
    {
        cout << m;
    }
}

void log::run()
{
    log::getInstance()->running = true;
    while (true)
    {
        while(!this->qu.empty())
        {
            lock_guard<mutex> lock(mtx);
            if(!this->qu.empty())
            {
                this->write(this->qu.front().msg,this->qu.front().ll);
                this->outfile.flush();
                this->qu.pop();
            }
        }
        if(this->qu.empty() && !log::getInstance()->running)
        {
            break;
        }
    }
    cout<<"end\n";
}

void log::start()
{
    thread t(&log::run,this);
    t.detach();
}

void log::stop()
{

    if(running)
    {
        running = false;
    }
}

void log::deleteInstance()
{
    if (log_ptr->outfile.is_open())
        log_ptr->outfile.close();
    //delete log_ptr;
    //log_ptr = nullptr;
}

void log::insert(string msg, log_level l1)
{
    logInfo data;
    data.ll = l1;
    data.msg = msg;
    qu.push(data);
}

void log::write(string msg, log_level l1)
{
    string tmp;
    if (l1 == log_level::debug)
        tmp = "[DEBUG]       ";
    else if (l1 == log_level::info)
        tmp = "[INFO]        ";
    else if (l1 == log_level::warn)
        tmp = "[WARN]        ";
    else if (l1 == log_level::error)
        tmp = "[ERROR]       ";
    else
    {
        return;
    }
    tmp += currentTime() + " : " + msg + "\n";
    if (lt != log_target::terminal)
        outfile << tmp;
    if (lt != log_target::file)
        cout << tmp;
}

log *log::getInstance(log_target l1, log_level l2, string p)
{
    if (log_ptr == nullptr)
    {
        lock_guard<mutex> lock(mtx);
        if (log_ptr == nullptr)
            log_ptr = new log(l1, l2, p);
    }
    return log_ptr;
}

log *log::log_ptr = nullptr;

mutex log::mtx;


int i = 0;

mutex mtx;


void write_in_log(log *l)
{
    lock_guard<mutex> lock(mtx);
    if (i % 4 == 0)
        l->debug("-debug-" + to_string(i));
    else if (i % 4 == 1)
        l->info("-info-" + to_string(i));
    else if (i % 4 == 2)
        l->warn("-warn-" + to_string(i));
    else if (i % 4 == 3)
        l->error("-error-" + to_string(i));
    i++;

}

int main()
{
    static log *l = log::getInstance(log_target::all, log_level::error);
    l->start();
    write_in_log(l);
    write_in_log(l);
    write_in_log(l);
    write_in_log(l);
    write_in_log(l);
    write_in_log(l);
    write_in_log(l);
    write_in_log(l);
    sleep(2);
    l->stop();
    log::deleteInstance();
    return 0;
}